﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CameraManager.cs" company="FocalSpec Ltd">
// FocalSpec Ltd 2016-
// </copyright>
// <summary>
// Controls FocalSpec sensor.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using FocalSpec.FsApiNet.Model;
using FocalSpec.GuiExample.Model.Export;
using static FocalSpec.FsApiNet.Model.FsApi;

namespace FocalSpec.GuiExample.Model.Camera
{
    /// <summary>
    /// Handles received profiles.
    /// </summary>
    /// <param name="profile">Profile to handle.</param>
    public delegate void PointCloudReceivedHandler(Profile profile);

    /// <summary>
    /// Handles received raw images.
    /// </summary>
    /// <param name="rawImage">Raw image to handle.</param>
    public delegate void RawImageReceivedHandler(RawImage rawImage);

    /// <summary>
    /// Handles camera selection.
    /// </summary>
    /// <param name="cameraIds">List of camera ids.</param>
    public delegate void CameraSelectionHandler(List<string> cameraIds);

    /// <summary>
    /// Provides easy access to camera.
    /// </summary>
    public class CameraManager
    {
        /// <summary>
        /// The camera instance.
        /// </summary>
        private FsApi _camera;

        public int layerId1;
        public float[] zValues1;
        public float[] intensityValues1;
        public int lineLength1;
        public double xStep1;
        public FsApi.Header header1;

        public int layerId2;
        public float[] zValues2;
        public float[] intensityValues2;
        public int lineLength2;
        public double xStep2;
        public FsApi.Header header2;

        public int layerId3;
        public float[] zValues3;
        public float[] intensityValues3;
        public int lineLength3;
        public double xStep3;
        public FsApi.Header header3;



        public List<string> cameraIds;

        /// <summary>
        /// Settings of the sensor.
        /// </summary>
        private readonly ApplicationSettings _settings;



        /// <summary>
        /// Identifier for the camera.
        /// </summary>
        /// 
        //camera2
        //public string _cameraId;

        //public string CameraId
        //{
        //    set => _cameraId = value;
        //}

        /// <summary>
        /// Flag that is set to true, when the program is about to exist.
        /// </summary>
        private bool _exitRequested;

        /// <summary>
        /// The queue for the point clouds. 
        /// </summary>
        private readonly ConcurrentQueue<Profile> _queue = new ConcurrentQueue<Profile>();

        /// <summary>
        /// Gets or sets the camera version.
        /// </summary>
        public string CameraVersion { get; private set; }

        /// <summary>
        /// Gets or sets the serial number of camera, derived from MAC address
        /// </summary>
        public string CameraSn { get; private set; }


        /// <summary>
        /// Gets or sets the LCI sensor device serial number.
        /// </summary>
        public string DeviceSerialNumber { get; private set; }

        /// <summary>
        /// Gets the length of the local reception queue.
        /// </summary>
        public int QueueLength
        {
            get { return _queue.Count; }
        }

        /// <summary>
        /// Gets or sets the flag indicating whether the infinite queue size is enabled or not. If enabled, then queue size can cumulate over time.
        /// </summary>
        public bool IsInfiniteQueueSizeEnabled { get; set; }

        /// <summary>
        /// Gets the flag indicating whether the camera buffer (reception buffer inside FSAPI) is empty or not.
        /// </summary>
        public bool IsCameraBufferEmpty
        {
            get { return !_queue.Any(); }
        }

        /// <summary>
        /// Gets the flag indicating whether the HW supports Automatic Gain Control (AGC) or not.
        /// </summary>
        public bool IsAgcSupported = false;
        //{
        //    //get
        //    //{
        //        _camera.GetParameterSupport(_cameraId, SensorParameter.AgcEnabled, out bool agcSupport);
        //        return agcSupport;
        //    //}
        //}

        /// <summary>
        /// Gets the flag indicating whether Automatic Gain Control (AGC) is enabled or not.
        /// </summary>
        public bool IsAgcEnabled
        {
            get
            {
                if (!IsAgcSupported)
                    return false;
                return _isAgcEnabled;
            }
            private set { _isAgcEnabled = value; }
        }

        /// <summary>
        /// Gets the flag indicating whether camera image is vertically flipped.
        /// </summary>
        public bool IsFlippedImage { get; private set; }

        /// <summary>
        /// LED pulse width [µs] adjusted by the Automatic Gain Control (AGC).
        /// </summary>
        private int _agcAdjustedLedPulseWidth;

        /// <summary>
        /// Gets the flag indicating if sensor is High Speed camera or not.
        /// </summary>
        public bool IsHsCamera
        {
            get
            {
                return _settings.SensorType > 1600;
            }
        }

        public int MaxSensorHeight
        {
            get
            {
                return _settings.SensorType > 1600 ? Defines.MaxHsSensorHeight : Defines.MaxSensorHeight;
            }
        }

        public bool IsXFilterSupported { get; set; }
        public bool IsHdrSupported { get; set; }

        /// <summary>
        /// Processes received profiles, if needed.
        /// </summary>
        private readonly ProfileProcessor _profileProcessor;

        /// <summary>
        /// Last received profile header.
        /// </summary>
        private FsApi.Header _header;


        /// <summary>
        /// Gets the current LED pulse width in µs. If Automatic Gain Control (AGC) is in use, the value returned was adjusted by the AGC.
        /// </summary>
        //public int CurrentLedPulseWidth
        //{
        //    get
        //    {
        //        if (IsAgcSupported && IsAgcEnabled)
        //        {
        //            return _agcAdjustedLedPulseWidth;
        //        }

        //        int pulseWidth;
        //        CameraStatusCode status = _camera.GetParameter(_cameraId, SensorParameter.LedDuration, out pulseWidth);
        //        if (status != CameraStatusCode.Ok)
        //        {
        //            // Should never happen though.
        //            throw new InvalidOperationException("Sensor connection issue.");
        //        }
        //        return pulseWidth;
        //    }
        //}

        private int _selectedLayerIndex = 0;

        private bool _layerChanged = false;

        public const int NotModified = -1;

        public int SelectedLayerIndex
        {
            get { return _selectedLayerIndex; }
            set
            {
                if (value != _selectedLayerIndex)
                {
                    //_camera.StopGrabbing(_cameraId);
                    Thread.Sleep(100);
                    _layerChanged = true;
                }

                _selectedLayerIndex = value;
            }
        }

        private ExportLayer _selectedLayer;
        private bool _isAgcEnabled;

        //public ExportLayer SelectedLayer
        //{
        //    get => this._selectedLayer;

        // set
        //    {
        //        if (Session.ViewMode == ViewMode.Recording || Session.ViewMode == ViewMode.RawImage)
        //            return;

        //     if (this._selectedLayer != value || _layerChanged)
        //      this._selectedLayer = value;
        //     else
        //     {
        //      return;
        //     }

        //        if (Session.ViewMode == ViewMode.RealTime)
        //        {
        //            _camera.StopGrabbing(_cameraId);
        //            Thread.Sleep(100);
        //        }

        //        switch (this._selectedLayer)
        //        {
        //            case ExportLayer.All:             
        //                this._camera.RemoveLineCallback(this._cameraId);
        //                _camera.SetProfileCallback(_cameraId, ProfileReceptionCallback);
        //                break;

        //            case ExportLayer.Top:
        //                _camera.RemoveLineCallback(_cameraId);
        //                _camera.RemoveProfileCallback(_cameraId);
        //                _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromTopToBottom);
        //                for (int i = 0; i <= SelectedLayerIndex; i++)
        //                    _camera.SetLineCallback(_cameraId, i, this.LineCallback);
        //                break;

        //            case ExportLayer.Bottom:
        //                _camera.RemoveLineCallback(_cameraId);
        //                _camera.RemoveProfileCallback(_cameraId);
        //                _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromBottomToTop);
        //                for (int i = 0; i <= SelectedLayerIndex; i++)
        //                    _camera.SetLineCallback(_cameraId, i, this.LineCallback);
        //                break;

        //            case ExportLayer.Brightest:
        //                _camera.RemoveLineCallback(_cameraId);
        //                _camera.RemoveProfileCallback(_cameraId);
        //                _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromMaxIntensityToLower);
        //                for (int i = 0; i <= SelectedLayerIndex; i++)
        //                    _camera.SetLineCallback(_cameraId, i, this.LineCallback);
        //                break;
        //            default: break;
        //        }

        //        if (Session.ViewMode == ViewMode.RealTime)
        //        {
        //            _camera.StartGrabbing(_cameraId);
        //        }

        //     _layerChanged = false;
        //    }
        //}

        /// <summary>
        /// Event fires when a point cloud is received from the camera.
        /// </summary>
        public event PointCloudReceivedHandler OnPointCloudReceivedEvent;

        /// <summary>
        /// Event fires when a raw image is received from the camera.
        /// </summary>
        public event RawImageReceivedHandler OnRawImageReceivedEvent;

        /// <summary>
        /// Event handler for the camera selection.
        /// </summary>
        //public event CameraSelectionHandler OnCameraSelectionEvent;

        /// <summary>
        /// Starts the reception thread.
        /// </summary>
        public CameraManager(ApplicationSettings settings)
        {
            _settings = settings;
            _profileProcessor = new ProfileProcessor();
            // _cameraId = null;
            IsInfiniteQueueSizeEnabled = false;

            var queueThread = new Thread(QueueThread) { Priority = ThreadPriority.Highest };
            queueThread.Start();
        }

        //camera2
        static int CameraCount { get; set; }
        public int cameraCount = CameraCount;



        /// <summary>
        /// Stop grabbing and close the camera instance.
        /// </summary>
        //public void Close(string _cameraId)
        //{
        //    _exitRequested = true;

        //    if (string.IsNullOrEmpty(_cameraId))
        //    {
        //        return;
        //    }

        //    _camera.StopGrabbing(_cameraId);
        //    Thread.Sleep(500);
        //    _camera.Close();
        //}

        public void Close()
        {
            _exitRequested = true;
            if (_camera != null)
            {
                Thread.Sleep(500);
                _camera.Close();
            }
        }

        //用于存放数据
        public List<Profile> profilePoints1 = new List<Profile>();
        public List<Profile> profilePoints2 = new List<Profile>();
        public List<Profile> profilePoints3 = new List<Profile>();
        public List<int> headerList = new List<int>();

        public void ClearList1()
        {
            profilePoints1.Clear();
        }
        public void ClearList2()
        {
            profilePoints2.Clear();
        }
        private void SaveProfileToList1(Profile profile)
        {

            profilePoints1.Add(profile);
            
        }
        private void SaveProfileToList2(Profile profile)
        {
            profilePoints2.Add(profile);
        }
        public void LineCallback1(
                   int layerId,
                   float[] zValues,
                   float[] intensityValues,
                   int lineLength,
                   double xStep,
                   FsApi.Header header)
        {
            layerId1 = layerId;
            zValues1 = zValues;
            intensityValues1 = intensityValues;
            lineLength1 = lineLength;
            xStep1 = xStep;
            header1 = header;

            if (_exitRequested)
            {
                return;
            }

            _header = header;
            Profile processed = new Profile(layerId, zValues, intensityValues, lineLength, xStep, header);
            SaveProfileToList1(processed);
            _queue.Enqueue(processed);
            headerList.Add(header.Location);
        }


        public void LineCallback2(
                   int layerId,
                   float[] zValues,
                   float[] intensityValues,
                   int lineLength,
                   double xStep,
                   FsApi.Header header)
        {
            layerId2 = layerId;
            zValues2 = zValues;
            intensityValues2 = intensityValues;
            lineLength2 = lineLength;
            xStep2 = xStep;
            header2 = header;

            if (_exitRequested)
            {
                return;
            }

            _header = header;
            Profile processed = new Profile(layerId, zValues, intensityValues, lineLength, xStep, header);
            SaveProfileToList2(processed);
            _queue.Enqueue(processed);



        }


        public void LineCallback3(
                   int layerId,
                   float[] zValues,
                   float[] intensityValues,
                   int lineLength,
                   double xStep,
                   FsApi.Header header)
        {
            layerId3 = layerId;
            zValues3 = zValues;
            intensityValues3 = intensityValues;
            lineLength3 = lineLength;
            xStep3 = xStep;
            header3 = header;

            if (_exitRequested)
            {
                return;
            }

            _header = header;
            Profile processed = new Profile(layerId, zValues, intensityValues, lineLength, xStep, header);
            SaveProfileToList1(processed);
            _queue.Enqueue(processed);

        }







        private void LineCallback(
            int layerId,
            float[] zValues,
            float[] intensityValues,
            int lineLength,
            double xStep,
            FsApi.Header header)
        {

            if (_exitRequested)
            {
                return;
            }

            _header = header;
            Profile processed = new Profile(layerId, zValues, intensityValues, lineLength, xStep, header);
            _queue.Enqueue(processed);


        }

        /// <summary>
        /// Enqueues profiles into an application buffer.
        /// </summary>
        /// <param name="profile">Profile received from the FSAPI.</param>
        /// <param name="header">Header for the point cloud.</param>
        private void ProfileReceptionCallback(IList<FsApi.Point> profile, FsApi.Header header)
        {
            if (_exitRequested)
            {
                return;
            }

            _header = header;

            try
            {
                Profile raw = new Profile(profile, header);
                Profile processed;

                // switch (SelectedLayer)
                // {
                //  case ExportLayer.All:
                processed = raw;
                //   break;

                // default:
                //  throw new ArgumentOutOfRangeException(string.Format("Unsupported layer {0}", SelectedLayer));
                //  }

                float average = 0;
                if (IsAgcEnabled)
                {
                    // AGC uses the entire signal regardless of filtering we do in the application.
                    if (raw.Points.Any())
                    {
                        foreach (var point in raw.Points)
                        {
                            average += point.Intensity;
                        }
                        average /= raw.Points.Count;
                    }
                }
                else
                {
                    if (processed.Points.Any())
                    {
                        foreach (var point in processed.Points)
                        {
                            average += point.Intensity;
                        }
                        average /= processed.Points.Count;
                    }
                }
                processed.AverageIntensity = average;

                _queue.Enqueue(processed);

            }
            catch (Exception)
            {
                // ignored
            }
        }

        private void RawImageCallback(byte[] image, int bytes, FsApi.Header header)
        {
            if (_exitRequested)
                return;

            _header = header;
            header.PulseWidth = _settings.LedPulseWidth;

            OnRawImageReceivedEvent?.Invoke(new RawImage(image, bytes, header, _settings.SensorWidth, IsFlippedImage));
        }

        public void LayerSelect1(ExportLayer _selectedLayer, string _cameraId)
        {
            //if (Session.ViewMode == ViewMode.RealTime)
            //{
            //    _camera.StopGrabbing(_cameraId);
            //    Thread.Sleep(100);
            //}

            _camera.StopGrabbing(_cameraId);
            Thread.Sleep(100);

            switch (_selectedLayer)
            {
                case ExportLayer.All:
                    this._camera.RemoveLineCallback(_cameraId);
                    _camera.SetProfileCallback(_cameraId, ProfileReceptionCallback);
                    break;

                case ExportLayer.Top:
                    _camera.RemoveLineCallback(_cameraId);
                    _camera.RemoveProfileCallback(_cameraId);
                    _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromTopToBottom);
                    for (int i = 0; i <= SelectedLayerIndex; i++)
                        _camera.SetLineCallback(_cameraId, i, this.LineCallback1);
                    break;

                case ExportLayer.Bottom:
                    _camera.RemoveLineCallback(_cameraId);
                    _camera.RemoveProfileCallback(_cameraId);
                    _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromBottomToTop);
                    for (int i = 0; i <= SelectedLayerIndex; i++)
                        _camera.SetLineCallback(_cameraId, i, this.LineCallback1);
                    break;

                case ExportLayer.Brightest:
                    _camera.RemoveLineCallback(_cameraId);
                    _camera.RemoveProfileCallback(_cameraId);
                    _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromMaxIntensityToLower);
                    for (int i = 0; i <= SelectedLayerIndex; i++)
                        _camera.SetLineCallback(_cameraId, i, this.LineCallback1);
                    break;
                default: break;
            }

           // _camera.StartGrabbing(_cameraId);
            //if (Session.ViewMode == ViewMode.RealTime)
            //{
            //    _camera.StartGrabbing(_cameraId);
            //}

        }

        public void LayerSelect2(ExportLayer _selectedLayer, string _cameraId)
        {
            SelectedLayerIndex = 0;
            //if (Session.ViewMode == ViewMode.RealTime)
            //{
            //    _camera.StopGrabbing(_cameraId);
            //    Thread.Sleep(100);
            //}

            _camera.StopGrabbing(_cameraId);
            Thread.Sleep(100);


            switch (_selectedLayer)
            {
                case ExportLayer.All:
                    this._camera.RemoveLineCallback(_cameraId);
                    _camera.SetProfileCallback(_cameraId, ProfileReceptionCallback);
                    break;

                case ExportLayer.Top:
                    _camera.RemoveLineCallback(_cameraId);
                    _camera.RemoveProfileCallback(_cameraId);
                    _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromTopToBottom);
                    for (int i = 0; i <= SelectedLayerIndex; i++)
                        _camera.SetLineCallback(_cameraId, i, this.LineCallback2);
                    break;

                case ExportLayer.Bottom:
                    _camera.RemoveLineCallback(_cameraId);
                    _camera.RemoveProfileCallback(_cameraId);
                    _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromBottomToTop);
                    for (int i = 0; i <= SelectedLayerIndex; i++)
                        _camera.SetLineCallback(_cameraId, i, this.LineCallback2);
                    break;

                case ExportLayer.Brightest:
                    _camera.RemoveLineCallback(_cameraId);
                    _camera.RemoveProfileCallback(_cameraId);
                    _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromMaxIntensityToLower);
                    for (int i = 0; i <= SelectedLayerIndex; i++)
                        _camera.SetLineCallback(_cameraId, i, this.LineCallback2);
                    break;
                default: break;
            }

        }


        public void LayerSelect3(ExportLayer _selectedLayer, string _cameraId)
        {
            SelectedLayerIndex = 0;
            //if (Session.ViewMode == ViewMode.RealTime)
            //{
            //    _camera.StopGrabbing(_cameraId);
            //    Thread.Sleep(100);
            //}

            _camera.StopGrabbing(_cameraId);
            Thread.Sleep(100);


            switch (_selectedLayer)
            {
                case ExportLayer.All:
                    this._camera.RemoveLineCallback(_cameraId);
                    _camera.SetProfileCallback(_cameraId, ProfileReceptionCallback);
                    break;

                case ExportLayer.Top:
                    _camera.RemoveLineCallback(_cameraId);
                    _camera.RemoveProfileCallback(_cameraId);
                    _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromTopToBottom);
                    for (int i = 0; i <= SelectedLayerIndex; i++)
                        _camera.SetLineCallback(_cameraId, i, this.LineCallback3);
                    break;

                case ExportLayer.Bottom:
                    _camera.RemoveLineCallback(_cameraId);
                    _camera.RemoveProfileCallback(_cameraId);
                    _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromBottomToTop);
                    for (int i = 0; i <= SelectedLayerIndex; i++)
                        _camera.SetLineCallback(_cameraId, i, this.LineCallback3);
                    break;

                case ExportLayer.Brightest:
                    _camera.RemoveLineCallback(_cameraId);
                    _camera.RemoveProfileCallback(_cameraId);
                    _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromMaxIntensityToLower);
                    for (int i = 0; i <= SelectedLayerIndex; i++)
                        _camera.SetLineCallback(_cameraId, i, this.LineCallback3);
                    break;
                default: break;
            }
            //_camera.StartGrabbing(_cameraId);

        }




        /// <summary>
        /// Initializes camera parameters.
        /// </summary>
        /// <returns>Operation outcome.</returns>
        /// 
        public CameraStatusCode DetectCamera()
        {


            _header.ReceptionQueueSize = 0;

            if (_camera == null)
                _camera = new FsApi();

            var cameraStatus = _camera.Open(ref cameraCount, out cameraIds, Defines.SensorDiscoveryTimeout);

            if (cameraStatus != CameraStatusCode.Ok)
                return cameraStatus;

            return cameraStatus;


        }


        public CameraStatusCode InitializeCamera(string _cameraId)
        {
            //         List<string> cameraIds;
            string cameraVersion, cameraSn;
            //         int cameraCount = _settings.ExpectedCameraCount;
            //         _header.ReceptionQueueSize = 0;

            //if(_camera == null)  
            //	_camera = new FsApi();

            //         var cameraStatus = _camera.Open(ref cameraCount, out cameraIds, Defines.SensorDiscoveryTimeout);

            //         if (cameraStatus != CameraStatusCode.Ok)
            //             return cameraStatus;

            //         switch (cameraCount)
            //         {
            //             case 0:
            //                 _camera.Close();
            //                 return CameraStatusCode.NotConnected;
            //             case 1:
            //                 _cameraId = cameraIds.First();
            //                 break;
            //             default:
            //                 _cameraId = cameraIds.First(); 
            //                 OnCameraSelectionEvent?.Invoke(cameraIds); // _cameraId is changed by the event
            //                 break;
            //         }

            var cameraStatus = _camera.Connect(_cameraId, _settings?.IpAddress);
            bool selfTestFailed = cameraStatus == CameraStatusCode.CameraErrorSelftestGrabbingFailed;
            if (selfTestFailed)
            {
                cameraStatus = CameraStatusCode.Ok;
            }

            if (cameraStatus != CameraStatusCode.Ok)
                return cameraStatus;

            cameraStatus = _camera.GetDeviceVersion(_cameraId, out cameraVersion);
            CameraVersion = cameraVersion;
            if (cameraStatus != CameraStatusCode.Ok)
                return cameraStatus;

            cameraStatus = _camera.GetSerialNumber(_cameraId, out cameraSn);
            CameraSn = cameraSn;
            if (cameraStatus != CameraStatusCode.Ok)
                return cameraStatus;

            int calibrationsInCamera;

            cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.SensorDataInFlash, out calibrationsInCamera);
            if (cameraStatus != CameraStatusCode.Ok)
                return cameraStatus;

            string deviceSerialNumber;
            cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.DeviceSerialNumber, out deviceSerialNumber);
            DeviceSerialNumber = cameraStatus == CameraStatusCode.Ok ? deviceSerialNumber : "";

            // Set maximum number of points per frame 
            _camera.SetParameter(_cameraId, SensorParameter.MaxPointCount, _settings.MaxPointCount);


            if (cameraIds.Count >= 1 && _cameraId == cameraIds[0])
            {
                if (calibrationsInCamera == 1)
                {
                    _settings.ZCalibrationFile = null;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakYUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    // read z calibration path from API and fill it to the settings 
                    string zcalibrationFile;
                    cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.ZCalibrationFile, out zcalibrationFile);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    _settings.ZCalibrationFile = zcalibrationFile;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.ZCalibrationFile, _settings.ZCalibrationFile);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakXUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    _settings.XCalibrationFile = null;
                    // read x calibration path from API and fill it to the settings 
                    string xcalibrationFile;
                    cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.XCalibrationFile, out xcalibrationFile);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    _settings.XCalibrationFile = xcalibrationFile;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.XCalibrationFile, _settings.XCalibrationFile);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;
                }
                if (calibrationsInCamera != 1 && _settings.ZCalibrationFile != null || _settings.XCalibrationFile != null)
                {
                    var zCalibStatus = _camera.SetParameter(_cameraId, SensorParameter.ZCalibrationFile, _settings.ZCalibrationFile);

                    if (zCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeNotFound ||
                        zCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeInvalid ||
                        zCalibStatus == CameraStatusCode.CameraErrorInvalidCalibrationFile)
                    {
                        // clear if not valid calibration.
                        _settings.ZCalibrationFile = null;
                    }

                    var xCalibStatus = _camera.SetParameter(_cameraId, SensorParameter.XCalibrationFile, _settings.XCalibrationFile);

                    if (xCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeNotFound ||
                        xCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeInvalid ||
                        xCalibStatus == CameraStatusCode.CameraErrorInvalidCalibrationFile)
                    {
                        // clear if not valid calibration.
                        _settings.XCalibrationFile = null;
                    }

                    if (zCalibStatus != CameraStatusCode.Ok)
                        return zCalibStatus;

                    if (xCalibStatus != CameraStatusCode.Ok)
                        return xCalibStatus;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakXUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakYUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                }


            }
            // ------------------------------------------------------------------  
            if (cameraIds.Count >= 2 && _cameraId == cameraIds[1])
            {
                if (calibrationsInCamera == 1)
                {

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakYUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    _settings.ZCalibrationFile2 = null;
                    // read z calibration path from API and fill it to the settings 
                    string zcalibrationFile2;
                    cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.ZCalibrationFile, out zcalibrationFile2);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    _settings.ZCalibrationFile2 = zcalibrationFile2;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.ZCalibrationFile, _settings.ZCalibrationFile2);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakXUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    _settings.XCalibrationFile2 = null;

                    // read x calibration path from API and fill it to the settings 
                    string xcalibrationFile2;
                    cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.XCalibrationFile, out xcalibrationFile2);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    _settings.XCalibrationFile2 = xcalibrationFile2;
                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.XCalibrationFile, _settings.XCalibrationFile2);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;
                }
                if (calibrationsInCamera != 1 && _settings.ZCalibrationFile2 != null || _settings.XCalibrationFile2 != null)
                {
                    var zCalibStatus = _camera.SetParameter(_cameraId, SensorParameter.ZCalibrationFile, _settings.ZCalibrationFile2);

                    if (zCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeNotFound ||
                        zCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeInvalid ||
                        zCalibStatus == CameraStatusCode.CameraErrorInvalidCalibrationFile)
                    {
                        // clear if not valid calibration.
                        _settings.ZCalibrationFile2 = null;
                    }

                    var xCalibStatus = _camera.SetParameter(_cameraId, SensorParameter.XCalibrationFile, _settings.XCalibrationFile2);

                    if (xCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeNotFound ||
                        xCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeInvalid ||
                        xCalibStatus == CameraStatusCode.CameraErrorInvalidCalibrationFile)
                    {
                        // clear if not valid calibration.
                        _settings.XCalibrationFile2 = null;
                    }


                    if (zCalibStatus != CameraStatusCode.Ok)
                        return zCalibStatus;

                    if (xCalibStatus != CameraStatusCode.Ok)
                        return xCalibStatus;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakXUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakYUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;
                }


            }
            // -------------------------------------------------------------------------------
            if (cameraIds.Count >= 3 && _cameraId == cameraIds[2])
            {
                if (calibrationsInCamera == 1)
                {

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakYUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    _settings.ZCalibrationFile3 = null;
                    // read z calibration path from API and fill it to the settings 
                    string zcalibrationFile3;
                    cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.ZCalibrationFile, out zcalibrationFile3);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    _settings.ZCalibrationFile3 = zcalibrationFile3;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.ZCalibrationFile, _settings.ZCalibrationFile3);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakXUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    _settings.XCalibrationFile3 = null;

                    // read x calibration path from API and fill it to the settings 
                    string xcalibrationFile3;
                    cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.XCalibrationFile, out xcalibrationFile3);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    _settings.XCalibrationFile3 = xcalibrationFile3;
                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.XCalibrationFile, _settings.XCalibrationFile3);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;
                }
                if (calibrationsInCamera != 1 && _settings.ZCalibrationFile3 != null || _settings.XCalibrationFile3 != null)
                {
                    var zCalibStatus = _camera.SetParameter(_cameraId, SensorParameter.ZCalibrationFile, _settings.ZCalibrationFile3);

                    if (zCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeNotFound ||
                        zCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeInvalid ||
                        zCalibStatus == CameraStatusCode.CameraErrorInvalidCalibrationFile)
                    {
                        // clear if not valid calibration.
                        _settings.ZCalibrationFile3 = null;
                    }

                    var xCalibStatus = _camera.SetParameter(_cameraId, SensorParameter.XCalibrationFile, _settings.XCalibrationFile3);

                    if (xCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeNotFound ||
                        xCalibStatus == CameraStatusCode.CameraErrorCalibFileAttributeInvalid ||
                        xCalibStatus == CameraStatusCode.CameraErrorInvalidCalibrationFile)
                    {
                        // clear if not valid calibration.
                        _settings.XCalibrationFile3 = null;
                    }


                    if (zCalibStatus != CameraStatusCode.Ok)
                        return zCalibStatus;

                    if (xCalibStatus != CameraStatusCode.Ok)
                        return xCalibStatus;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakXUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;

                    cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakYUnit, Defines.PeakUnitMicrometer);
                    if (cameraStatus != CameraStatusCode.Ok)
                        return cameraStatus;
                }




            }

            //---------------------------------------------------------------
            //cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakYUnit, Defines.PeakUnitMicrometer);
            //if (cameraStatus != CameraStatusCode.Ok)
            //    return cameraStatus;



            //cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PeakXUnit, Defines.PeakUnitMicrometer);
            //if (cameraStatus != CameraStatusCode.Ok)
            //    return cameraStatus;


            int currentSensor;
            cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.SensorType, out currentSensor);
            _settings.SensorType = currentSensor;
            if (cameraStatus != CameraStatusCode.Ok)
                return cameraStatus;

            if (IsHsCamera && !_settings.HdrEnabled)
            {
                _settings.Kp1Pos = 95;
                _settings.VLow2 = 33;
            }

            cameraStatus = SetWindowScale(_cameraId);
            if (cameraStatus != CameraStatusCode.Ok)
            {
                return cameraStatus;
            }

            // PulseDivider and TriggerSource are read from GuiExample.json file
            // due to case pin 2 is also used for triggering, disable source needs to be changed from 2 to 0
            // ignore error caused by sensors with old electronics 
            cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.TriggerSource, _settings.TriggerSource);
            if (cameraStatus != CameraStatusCode.Ok && cameraStatus != CameraStatusCode.CameraErrorHwNotSupported)
                return cameraStatus;

            cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.TriggerDisableSource, Defines.DefaultTriggerDisableSource);
            if (cameraStatus != CameraStatusCode.Ok && cameraStatus != CameraStatusCode.CameraErrorHwNotSupported)
                return cameraStatus;

            cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PulseDivider, _settings.PulseDivider);
            if (cameraStatus != CameraStatusCode.Ok && cameraStatus != CameraStatusCode.CameraErrorHwNotSupported)
                return cameraStatus;

            _camera.SetParameter(_cameraId, SensorParameter.PulseCurrentMiddle, _settings.Current);
            _camera.SetParameter(_cameraId, SensorParameter.PulseCurrentEdges, _settings.Current);

            cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.Gain, !IsHsCamera ? _settings.Gain : _settings.GainHs);
            if (cameraStatus != CameraStatusCode.Ok)
                return cameraStatus;

            // Enable noise removal for the line callback
            //_camera.SetParameter(_cameraId, SensorParameter.NoiseRemoval, 1);

            // These are important to get reliable data transfer. 
            //  1.	Enable Jumbo Frames in the PC’s Ethernet adapter by setting the value 9014 Bytes for the Packet size
            //	2.	Set Receive Buffer Size in the PC Ethernet adapter to 2048

            // If you cannot enable jumbo frames set this to 1500
            if (!IsHsCamera)
            {   // set parameters only if not high speed camera
                cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.Mtu, 4500);
                if (cameraStatus != CameraStatusCode.Ok)
                    return cameraStatus;

                // Set Interpacket Delay, if needed. By default, it's 20.
                cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.Ifg, 20);
                if (cameraStatus != CameraStatusCode.Ok)
                    return cameraStatus;
            }

            /*
            _camera.SetParameter(_cameraId, SensorParameter.HdrEnabled, 1);
            _camera.SetParameter(_cameraId, SensorParameter.Vlow2, 114);
            _camera.SetParameter(_cameraId, SensorParameter.VLow3, 116);
            _camera.SetParameter(_cameraId, SensorParameter.Kp1Pos, 10);
            _camera.SetParameter(_cameraId, SensorParameter.Kp2Pos, 25);

            SetPulseWidth(_settings.LedPulseWidth, _settings.MaxLedPulseWidth);
            */

            /*
            _camera.SetParameter(_cameraId, SensorParameter.DetectMissingFirstLayer, 100.0);
            _camera.SetParameter(_cameraId, SensorParameter.DetectMissingFirstLayerX, 100.0);
            _camera.SetParameter(_cameraId, SensorParameter.DetectMissingFirstLayerLength, 2500.0);
            */

            /*  
            cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.ResampleLineXResolution, 20.0);
            if (cameraStatus != CameraStatusCode.Ok) 
                return cameraStatus;

            cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.AverageZFilterSize, 20.0);
            if (cameraStatus != CameraStatusCode.Ok)
                return cameraStatus;
            cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.AverageIntensityFilterSize, 40.0);
            if (cameraStatus != CameraStatusCode.Ok)
                return cameraStatus;
            */

            // This is sample code section for dynamic sensor control parameter functionality
            /* <example> 

	        // Initialize dynamic parameter structure array up to 10 parameter sets
	        FsApi.DynSensorControl[] myDynamicSetting = 
	        {	// encoder location, image height, image offset, led pulse width
				new FsApi.DynSensorControl(1,     700, 294, 8.0f),
		        new FsApi.DynSensorControl(10000, 600, 244, 2.0f),
		        new FsApi.DynSensorControl(20000, 500, 294, 5.0f),
		        new FsApi.DynSensorControl(30000, 400, 344, 10.0f),
		        new FsApi.DynSensorControl(40000, 300, 394, 20.0f),
		        new FsApi.DynSensorControl(50000, 600, 244, 40.0f)
	        };
	        // Initialize version 2 of dynamic parameter structure array
			// if Threshold is included, supported only by HS CAM
	        FsApi.DynSensorControl[] newDynamicSetting = 
	        {	// encoder location, image height, image offset, led pulse width, Threshold
			    new FsApi.DynSensorControl(1,     700, 350, 8.0f,    24),
		        new FsApi.DynSensorControl(10000, 600, 400, 20.0f,   20),
		        new FsApi.DynSensorControl(20000, 500, 450, 50.0f,   25),
		        new FsApi.DynSensorControl(30000, 400, 500, 80.0f,   30),
		        new FsApi.DynSensorControl(40000, 300, 550, 100.0f,  20),
		        new FsApi.DynSensorControl(60000, 200, 600, 30.0f,   25)
	        };

	        // pass parameters to the camera
            cameraStatus = _camera.DynamicSensorCtrlParameters(_cameraId, newDynamicSetting.Length, newDynamicSetting);
	        // cameraStatus = _camera.DynamicSensorCtrlParameters(_cameraId, myDynamicSetting.Length, myDynamicSetting);
	        
	        // enable functionality
            cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.DynSensorCtrlEnable, 1);
			// cameraStatus not checked feature might not be supported
	        // </example> */

            //cameraStatus = _camera.SetProfileCallback(_cameraId, ProfileReceptionCallback);
            //if (cameraStatus != CameraStatusCode.Ok)
            // return cameraStatus;
            if (_cameraId == cameraIds[0])
            {
                LayerSelect1(ExportLayer.Top, _cameraId);
            }
            else if (_cameraId == cameraIds[1])
            {
                LayerSelect2(ExportLayer.Top, _cameraId);
            }
            else
            {
                LayerSelect3(ExportLayer.Top, _cameraId);
            }

            //camera2
            //if (IsAgcSupported)
            //{
            //    cameraStatus = EnableAgc(_settings.IsAgcEnabled, _cameraId);
            //    switch (cameraStatus)
            //    {
            //        case CameraStatusCode.CameraErrorHwNotSupported:
            //        case CameraStatusCode.CameraErrorFirmwareNotSupporting:
            //            break;
            //        case CameraStatusCode.Ok:
            //            if (_settings.IsAgcEnabled)
            //            {
            //                cameraStatus = SetAgcTargetIntensity(_settings.AgcTargetIntensity, _cameraId);
            //                if (cameraStatus != CameraStatusCode.Ok)
            //                    return cameraStatus;
            //            }

            //            break;
            //        default:
            //            return cameraStatus;
            //    }
            //}

            int flip;
            cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.FlipXEnabled, out flip);
            IsFlippedImage = flip == 1;
            //cameraStatus = _camera.StartGrabbing(_cameraId);

            if (selfTestFailed)
            {
                return CameraStatusCode.CameraErrorSelftestGrabbingFailed;
            }

            return cameraStatus;
        }

        /// <summary>
        /// Starts profile data acquisition.
        /// </summary>
        /// <param name="timeToSleep">Defines time to sleep [ms] before start grabbing. Is needed when sensor parameters are adjusted and grabbing stared right after </param>
        /// <returns> 0 if successful, any other positive value if failed or -1 if not performed because camera was already grabbing.</returns>
        public int StartGrabbing(int timeToSleep, string _cameraId)
        {
            int isGrabbing;
            var status = (int)_camera.GetParameter(_cameraId, SensorParameter.Grabbing, out isGrabbing);

            if (timeToSleep > 0)
                Thread.Sleep(timeToSleep);

            if (status != 0)
            {
                return status;
            }

            if (isGrabbing == 1)
            {
                status = NotModified;
            }
            else
            {
                status = (int)_camera.StartGrabbing(_cameraId);
            }

            return status;
        }

        /// <summary>
        /// Stops profile data acquisition.
        /// </summary>
        /// <param name="timeToSleep">Defines time to sleep [ms] after stop grabbing. Is needed when sensor parameters are adjusted and grabbing stared right after </param>
        /// <returns> 0 if successful, any other positive value if failed or -1 if not performed because camera was not grabbing.</returns>
        public int StopGrabbing(int timeToSleep, string _cameraId)
        {
            int isGrabbing;
            var status = (int)_camera.GetParameter(_cameraId, SensorParameter.Grabbing, out isGrabbing);
            header1 = new Header();
            header2 = new Header();
            header3 = new Header();
            if (status != 0)
            {
                return status;
            }

            if (isGrabbing == 0)
            {
                status = NotModified;
            }
            else
            {
                status = (int)_camera.StopGrabbing(_cameraId);
            }

            if (timeToSleep > 0)
                Thread.Sleep(timeToSleep);

            return status;
        }

        /// <summary>
        /// Queue thread. Check if there are data available in the queue. If there is, get the
        /// latest point cloud and call the event handlers.
        /// </summary>
        private void QueueThread()
        {
            // Loop until the program quits.
            while (!_exitRequested)
            {
                try
                {
                    Profile profile = null;

                    if (_queue.Any())
                    {
                        if (!_queue.TryDequeue(out profile))
                        {
                            Thread.Sleep(0);
                        }
                    }
                    else
                    {
                        Thread.Sleep(1);
                    }

                    try
                    {
                        if (OnPointCloudReceivedEvent != null && profile != null)
                        {
                            OnPointCloudReceivedEvent(profile);
                        }
                    }
                    catch
                    {
                        // ignored
                    }
                }
                catch
                {
                    // ignored
                }
            }
        }

        /// <summary>
        /// Sets frequency to the camera.
        /// </summary>
        /// <param name="freq">Sampling frequency [hz]. For external triggering, this will be the intended frequency, which is used to set up ROI.</param>
        /// <param name="isExternalPulsingEnabled">If true, 0 (value for external) will be written to sensor.</param>
        /// <returns>Status code indicating operation outcome.</returns>
        public CameraStatusCode SetFreq(int freq, bool isExternalPulsingEnabled, string _cameraId)
        {
            var cameraStatus = CameraStatusCode.Ok;

            // nothing to do here is frequency has not been defined
            if (freq <= 0)
            {
                return cameraStatus;
            }

            // do not set delay less than 100 ms, otherwise sensor height setting does fail 
            var grabbingStatus = StopGrabbing(110, _cameraId);

            // Previous implementation for calculating imaging height and offset replaced by this interface function.
            // HeightZeroAdjustEnabled is used for offset setting automatically. 
            int zHeight;
            if (_settings.HeightZeroAdjustEnabled)
            {

                cameraStatus = _camera.AdjustRoiAndFps(_cameraId, (int)(RoiOperation.SetRoiForFps), freq, out zHeight);
                if (cameraStatus != CameraStatusCode.Ok)
                {
                    return cameraStatus;
                }
            }
            else
            {
                cameraStatus = _camera.AdjustRoiAndFps(_cameraId, (int)(RoiOperation.SetRoiForFpsCentered), freq, out zHeight);
                if (cameraStatus != CameraStatusCode.Ok)
                {
                    return cameraStatus;
                }
            }

            if (Session.ViewMode == ViewMode.RawImage)
            {   // do not use high frequencies while viewing raw image
                freq = freq < 10 ? freq : 10;
                isExternalPulsingEnabled = false;
            }

            if (isExternalPulsingEnabled)
            {
                freq = Defines.ExternalTriggering;
            }

            cameraStatus = _camera.SetParameter(_cameraId, SensorParameter.PulseFrequency, isExternalPulsingEnabled ? 0 : freq);

            if (grabbingStatus != NotModified)
                StartGrabbing(20, _cameraId);

            return cameraStatus;
        }

        /// <summary>
        /// Sets LED pulse width. Utilizes Automatic Gain Control (AGC) if it is supported by the HW.
        /// </summary>
        /// <param name="us">LED pulse width in µs. If AGC is available, this is used as starting reference.</param>
        /// <param name="maxUs">If AGC is available, this is the max. pulse width AGC can adjust into. Otherwise this is null.</param>
        /// <exception cref="ArgumentException">Start LED pulse is higher than max. LED pulse.</exception>
        /// <returns>Status code indicating operation outcome.</returns>
        public CameraStatusCode SetPulseWidth(int us, int? maxUs, string _cameraId)
        {
            CameraStatusCode status;

            int grabbingStatus = StopGrabbing(20, _cameraId);

            if (maxUs.HasValue && IsAgcEnabled)
            {
                if (us > maxUs.Value)
                    throw new ArgumentException("LED pulse start reference must be smaller than LED pulse upper limit (max).");

                status = _camera.SetParameter(_cameraId, SensorParameter.AgcWiLimit, maxUs.Value);

                if (status != CameraStatusCode.Ok)
                {
                    if (grabbingStatus != NotModified)
                        StartGrabbing(20, _cameraId);
                    return status;
                }
            }

            status = _camera.SetParameter(_cameraId, SensorParameter.LedDuration, us);
            if (grabbingStatus != NotModified)
                StartGrabbing(20, _cameraId);
            return status;
        }

        /// <summary>
        /// 设置触发减频倍数
        /// </summary>
        /// <param name="divider"></param>
        /// <returns></returns>
        public CameraStatusCode SetPulseDivider(int divider, string _cameraId)
        {
            CameraStatusCode status = _camera.SetParameter(_cameraId, SensorParameter.PulseDivider, divider);
            return status;
        }

        public CameraStatusCode SetXResampling(float xResolution, string _cameraId)
        {
            CameraStatusCode status = _camera.SetParameter(_cameraId, SensorParameter.ResampleLineXResolution, xResolution);
            return status;
        }
        /// <summary>
        /// Enables/disables AGC.
        /// </summary>
        /// <param name="isEnabled">If true, tries to enable AGC.</param>
        /// <returns>CameraStatusCode.Ok if AGC is supported and enabled.</returns>
        public CameraStatusCode EnableAgc(bool isEnabled, string _cameraId)
        {
            if (IsAgcEnabled == isEnabled || (Session.ViewMode == ViewMode.RawImage))
                return CameraStatusCode.Ok;

            int grabbingStatus = StopGrabbing(30, _cameraId);

            CameraStatusCode status = _camera.SetParameter(_cameraId, SensorParameter.AgcEnabled, isEnabled ? 1 : 0);

            if (status == CameraStatusCode.Ok)
                IsAgcEnabled = isEnabled;

            if (grabbingStatus != NotModified)
                StartGrabbing(20, _cameraId);

            return status;
        }

        /// <summary>
        /// Sets AGC target intensity in greyscale.
        /// </summary>
        /// <param name="targetIntensity">Target intensity [0.0-255.0]</param>
        /// <returns>Status code indicating operation outcome.</returns>
        public CameraStatusCode SetAgcTargetIntensity(float targetIntensity, string _cameraId)
        {
            if (Session.ViewMode == ViewMode.RawImage)
                return CameraStatusCode.Ok;

            int grabbingStatus = StopGrabbing(30, _cameraId);
            var status = _camera.SetParameter(_cameraId, SensorParameter.AgcTarget, targetIntensity);

            if (grabbingStatus != NotModified)
                StartGrabbing(20, _cameraId);

            return status;
        }

        /// <summary>
        /// Sets calibrated axis offset and width in pixels.
        /// </summary>
        /// <returns>Status code indicating operation outcome.</returns>
        public CameraStatusCode SetWindowScale(string _cameraId)
        {
            // include check if supported by FW
            _camera.SetLineSortingOrder(_cameraId, SortingOrder.FromTopToBottom);
            _camera.SetLineCallback(_cameraId, 0, LineCallback);

            _settings.OpticalProfileMinX = 0;

            int width;
            var cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.Width, out width);
            if (cameraStatus != CameraStatusCode.Ok)
            {
                return cameraStatus;
            }
            _settings.SensorWidth = width;

            float piXRes;
            cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.LineCallbackXRes, out piXRes);
            if (cameraStatus != CameraStatusCode.Ok)
            {
                return cameraStatus;
            }
            _settings.AveragePixelWidth = piXRes * 0.001;
            _settings.OpticalProfileMaxX = _settings.AveragePixelWidth * width;

            // not yet used
            int offsetX;
            cameraStatus = _camera.GetParameter(_cameraId, SensorParameter.OffsetX, out offsetX);
            _settings.XOffset = offsetX;

            _camera.RemoveLineCallback(_cameraId);

            return cameraStatus;
        }

        /// <summary>
        /// Sets fir length.
        /// </summary>
        /// <param name="firLength">Fir Length [2,4,8,16]</param>
        /// <returns>Status code indicating operation outcome.</returns>
        public CameraStatusCode SetFirLength(int firLength, string _cameraId)
        {
            if (Session.ViewMode == ViewMode.RawImage)
                return CameraStatusCode.Ok;

            int grabbingStatus = StopGrabbing(30, _cameraId);
            var status = _camera.SetParameter(_cameraId, SensorParameter.FirLength, firLength);
            if (grabbingStatus != NotModified)
                StartGrabbing(20, _cameraId);
            return status;
        }
        //Set XFilter
        public CameraStatusCode SetXFilter(int xFilter, string _cameraId)
        {
            
            int grabbingStatus = StopGrabbing(30, _cameraId);
            var status = _camera.SetParameter(_cameraId, SensorParameter.PeakXFilter, xFilter);
            //if (grabbingStatus != NotModified)
               // StartGrabbing(20, _cameraId);
            return status;
        }

        /// <summary>
        /// Sets averaging fir length.
        /// </summary>
        /// <param name="averagingFirLength">Averaging Fir Length [2,4,8,16]</param>
        /// <returns>Status code indicating operation outcome.</returns>
        public CameraStatusCode SetAveragingFirLength(int averagingFirLength, string _cameraId)
        {
            if (Session.ViewMode == ViewMode.RawImage)
                return CameraStatusCode.Ok;

            int grabbingStatus = StopGrabbing(30, _cameraId);
            var status = _camera.SetParameter(_cameraId, SensorParameter.FirAverLength, averagingFirLength);
            if (grabbingStatus != NotModified)
                StartGrabbing(20, _cameraId);
            return status;
        }

        /// <summary>
        /// Sets peak core threshold.
        /// </summary>
        /// <param name="threshold">Peak core threshold [0-255]</param>
        /// <returns>Status code indicating operation outcome.</returns>
        public CameraStatusCode SetThreshold(int threshold, string _cameraId)
        {
            if (Session.ViewMode == ViewMode.RawImage)
                return CameraStatusCode.Ok;

            int grabbingStatus = StopGrabbing(30, _cameraId);
            var status = _camera.SetParameter(_cameraId, SensorParameter.PeakThreshold, threshold);
            if (grabbingStatus != NotModified)
                StartGrabbing(20, _cameraId);
            return status;
        }

        //Add median filter in pixels  20200311
        public CameraStatusCode MedianFilter(int pixels, string _cameraId)
        {
            //if (Session.ViewMode == ViewMode.RawImage)
            //    return CameraStatusCode.Ok;

            int grabbingStatus = StopGrabbing(30, _cameraId);
            var status = _camera.SetParameter(_cameraId, SensorParameter.MedianZFilterSize, pixels);
            //if (grabbingStatus != NotModified)
            //    StartGrabbing(20, _cameraId);
            return status;
        }

        /// <summary>
        /// Checks if individual parameter is supported by FSSDK, harware and firmware.
        /// </summary>
        /// <param name="parameterName">Name of sensor parameter</param>
        /// <returns>Status code indicating operation outcome.</returns>
        public bool IsParameterSupported(string parameterName, string _cameraId)
        {
            bool isSupported;
            var status = _camera.GetParameterSupport(_cameraId, parameterName, out isSupported);
            return isSupported;
        }

        /// <summary>
        /// Sets raw image mode
        /// </summary>
        /// <param name="rawImageMode">Raw image mode on off</param>
        /// <param name="freq">Sampling frequency [hz]. For external triggering, this will be the intended frequency, which is used to set up ROI.</param>
        /// <param name="isExternalPulsingEnabled">If true, 0 (value for external) will be written to sensor.</param>
        /// <returns>Status code indicating operation outcome.</returns>
        public CameraStatusCode SetRawImageMode(bool rawImageMode, int freq, bool isExternalPulsingEnabled, string _cameraId)
        {
            StopGrabbing(100, _cameraId);
            CameraStatusCode status = CameraStatusCode.Ok;
            if (rawImageMode)
            {
                Session.ViewMode = ViewMode.RawImage;
                if (_selectedLayer == ExportLayer.All)
                {
                    _camera.RemoveProfileCallback(_cameraId);
                }
                else
                {
                    _camera.RemoveLineCallback(_cameraId);
                }
                status = _camera.SetParameter(_cameraId, SensorParameter.PeakEnabled, 0);
                if (status == CameraStatusCode.Ok)
                {
                    _camera.SetRawImageCallback(_cameraId, RawImageCallback);
                }
            }
            else
            {
                Session.ViewMode = ViewMode.RealTime;
                _camera.RemoveRawImageCallback(_cameraId);
                status = _camera.SetParameter(_cameraId, SensorParameter.PeakEnabled, 1);

                if (_selectedLayer == ExportLayer.All)
                {
                    _camera.SetProfileCallback(_cameraId, ProfileReceptionCallback);
                }
                else
                {
                    _camera.SetLineCallback(_cameraId, SelectedLayerIndex, LineCallback);
                }
            }

            EnableAgc(_settings.IsAgcEnabled & !rawImageMode, _cameraId);

            if (status == CameraStatusCode.Ok)
                status = SetFreq(freq, isExternalPulsingEnabled, _cameraId);

            StartGrabbing(20, _cameraId);
            return status;
        }

        /// <summary>
        /// Sets High dynamic range (HDR) for camera.
        /// </summary>
        /// <param name="hdr">HDR enabled/disabled</param>
        /// <param name="vLow2">vLow2</param>
        /// <param name="vLow3">vLow3</param>
        /// <param name="kp1Pos">kp1Pos</param>
        /// <param name="kp2Pos">kp2Pos</param>
        /// <returns>Status code indicating operation outcome.</returns>
        public CameraStatusCode SetHdr(bool hdr, int vLow2, int vLow3, int kp1Pos, int kp2Pos, string _cameraId)
        {
            var status = _camera.SetParameter(_cameraId, SensorParameter.HdrEnabled, hdr ? 1 : 0);
            if (hdr)
            {
                _camera.SetParameter(_cameraId, SensorParameter.HdrEnabled, 0);
                if (status == CameraStatusCode.Ok)
                    status = _camera.SetParameter(_cameraId, SensorParameter.Vlow2, vLow2);
                if (status == CameraStatusCode.Ok && !IsHsCamera)
                    status = _camera.SetParameter(_cameraId, SensorParameter.VLow3, vLow3);
                if (status == CameraStatusCode.Ok)
                    status = _camera.SetParameter(_cameraId, SensorParameter.Kp1Pos, kp1Pos);
                if (status == CameraStatusCode.Ok && !IsHsCamera)
                    status = _camera.SetParameter(_cameraId, SensorParameter.Kp2Pos, kp2Pos);
                _camera.SetParameter(_cameraId, SensorParameter.HdrEnabled, 1);
            }

            return status;
        }

        /// <summary>
        /// Reads sensor temperature.
        /// </summary>
        /// <param name="sensorChipTemperature">Sensor chip temperature</param>
        /// <param name="sensorBoardTemperature">Sensor board temperature</param>
        /// <param name="illuminatorTemperature">Illuminator temperature</param>
        /// <param name="frontPanelTemperature">Front panel temperature</param>
        /// <returns>Status code indicating operation outcome.</returns>
        public CameraStatusCode GetTemperature(out float sensorChipTemperature, out float sensorBoardTemperature, out float illuminatorTemperature, out float frontPanelTemperature, string _cameraId)
        {
            sensorChipTemperature = sensorBoardTemperature = illuminatorTemperature = frontPanelTemperature = 0;
            CameraStatusCode status;
            if (IsHsCamera)
            {
                status = _camera.GetParameter(_cameraId, SensorParameter.ChipTemperature, out sensorChipTemperature);
                if (status == CameraStatusCode.Ok)
                    status = _camera.GetParameter(_cameraId, SensorParameter.SensorBoardTemperatureFloat, out sensorBoardTemperature);
                if (status == CameraStatusCode.Ok)
                    status = _camera.GetParameter(_cameraId, SensorParameter.IlluminatorTemperature, out illuminatorTemperature);
                if (status == CameraStatusCode.Ok)
                    status = _camera.GetParameter(_cameraId, SensorParameter.FrontPanelTemperature, out frontPanelTemperature);
            }
            else
            {
                status = _camera.GetParameter(_cameraId, SensorParameter.SensorTemperature, out sensorChipTemperature);
                if (status == CameraStatusCode.Ok)
                    status = _camera.GetParameter(_cameraId, SensorParameter.SensorBoardTemperature, out sensorBoardTemperature);
            }
            return status;
        }

        /// <summary>
        /// Writes filter values to sensor.
        /// </summary>
        /// <param name="noiseRemoval">Noise removal on/off.</param>
        /// <param name="averageZ">Average Z in µm.</param>
        /// <param name="averageIntensity">Average intensity in µm.</param>
        /// <param name="medianZ">Median Z in pixels.</param>
        /// <param name="medianIntensity">Median intensity in pixels.</param>
        /// <param name="resample">Re-sample X-resolution in µm.</param>
        /// <param name="peakXFilter">Peak X-filter length</param>
        public CameraStatusCode SetFilterValues(bool noiseRemoval, double averageZ, double averageIntensity, int medianZ, int medianIntensity, double resample, int peakXFilter, string _cameraId)
        {
            StopGrabbing(30, _cameraId);

            var status = _camera.SetParameter(_cameraId, SensorParameter.NoiseRemoval, noiseRemoval ? 1 : 0);
            if (status == CameraStatusCode.Ok)
                status = _camera.SetParameter(_cameraId, SensorParameter.AverageZFilterSize, averageZ);
            if (status == CameraStatusCode.Ok)
                status = _camera.SetParameter(_cameraId, SensorParameter.AverageIntensityFilterSize, averageIntensity);
            if (status == CameraStatusCode.Ok)
                status = _camera.SetParameter(_cameraId, SensorParameter.MedianZFilterSize, medianZ);
            if (status == CameraStatusCode.Ok)
                status = _camera.SetParameter(_cameraId, SensorParameter.MedianIntensityFilterSize, medianIntensity);
            if (status == CameraStatusCode.Ok)
                status = _camera.SetParameter(_cameraId, SensorParameter.ResampleLineXResolution, resample);
            if (status == CameraStatusCode.Ok && IsXFilterSupported)
                status = _camera.SetParameter(_cameraId, SensorParameter.PeakXFilter, peakXFilter);

            StartGrabbing(20, _cameraId);
            return status;
        }

        public int FlushQueue(int idleTime, string _cameraId)
        {
            return (int)_camera.SetParameter(_cameraId, SensorParameter.FlushQueue, idleTime);
        }

        /// <summary>
        /// Sets filter values for peak detection and peak average intensity filters for camera.
        /// </summary>
        /// <param name="peakDetection">Filter to reduce noise at low peak values</param>
        /// <param name="averageIntensity">Filter for averaging intensity values of the detected peaks</param>
        /// <returns>Status code indicating operation outcome.</returns>
        //public CameraStatusCode SetPeakDetectionFilterLength(int peakDetection, int averageIntensity, string _cameraId)
        //{
        //    CameraStatusCode status = _camera.SetParameter(_cameraId, SensorParameter.PeakDetectionFilterLength, peakDetection);
        //    if (status == CameraStatusCode.Ok)
        //        status = _camera.SetParameter(_cameraId, SensorParameter.PeakAverageIntensityFilterLength, averageIntensity);
        //    return status;
        //}
       
    }
}
